/*
* 11/26/04 Buffer size modified to support JRE 1.5 optimizations.
* (CPU usage < 1% under P4/2Ghz, RAM < 12MB).
* jlayer@javazoom.net
* 11/19/04 1.0 moved to LGPL.
* 06/04/01 Too fast playback fixed. mdm@techie.com
* 29/01/00 Initial version. mdm@techie.com
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package javazoom.jl.player;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.FloatControl;
import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.JavaLayerException;
/**
* The <code>JavaSoundAudioDevice</code> implements an audio
* device by using the JavaSound API.
*
* @since 0.0.8
* @author Mat McGowan
*/
public class JavaSoundAudioDevice extends AudioDeviceBase {
private SourceDataLine source = null;
private SourceDataLine testSource = null;
private AudioFormat fmt = null;
private byte[] byteBuf = new byte[4096];
private float maxGain = -99999.0f;
private float minGain = -99999.0f;
protected void setAudioFormat(AudioFormat fmt0) {
fmt = fmt0;
}
protected AudioFormat getAudioFormat() {
if (fmt == null) {
Decoder decoder = getDecoder();
fmt = new AudioFormat(decoder.getOutputFrequency(),
16,
decoder.getOutputChannels(),
true,
false);
}
return fmt;
}
protected DataLine.Info getSourceLineInfo() {
AudioFormat fmt = getAudioFormat();
//DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, 4000);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
return info;
}
public void open(AudioFormat fmt) throws JavaLayerException {
if (!isOpen()) {
setAudioFormat(fmt);
openImpl();
setOpen(true);
}
}
protected void openImpl()
throws JavaLayerException {
}
protected void createSource() throws JavaLayerException {
createSource2(true);
}
// createSource fix.
protected void createSource2(boolean start) throws JavaLayerException {
Throwable t = null;
try {
Line line = AudioSystem.getLine(getSourceLineInfo());
if (line instanceof SourceDataLine) {
source = (SourceDataLine) line;
//source.open(fmt, millisecondsToBytes(fmt, 2000));
source.open(fmt);
/*
if (source.isControlSupported(FloatControl.Type.MASTER_GAIN))
{
FloatControl c = (FloatControl)source.getControl(FloatControl.Type.MASTER_GAIN);
c.setValue(c.getMaximum());
}*/
if(start) {
source.start();
}
}
} catch (RuntimeException ex) {
t = ex;
} catch (LinkageError ex) {
t = ex;
} catch (LineUnavailableException ex) {
t = ex;
}
if (source == null) {
throw new JavaLayerException("cannot obtain source audio line", t);
}
}
public int millisecondsToBytes(AudioFormat fmt, int time) {
return (int) (time * (fmt.getSampleRate() * fmt.getChannels() * fmt.getSampleSizeInBits()) / 8000.0);
}
protected void closeImpl() {
if (source != null) {
source.close();
}
}
protected void writeImpl(short[] samples, int offs, int len)
throws JavaLayerException {
if (source == null) {
createSource();
}
byte[] b = toByteArray(samples, offs, len);
source.write(b, 0, len * 2);
}
protected byte[] getByteArray(int length) {
if (byteBuf.length < length) {
byteBuf = new byte[length + 1024];
}
return byteBuf;
}
protected byte[] toByteArray(short[] samples, int offs, int len) {
byte[] b = getByteArray(len * 2);
int idx = 0;
short s;
while (len-- > 0) {
s = samples[offs++];
b[idx++] = (byte) s;
b[idx++] = (byte) (s >>> 8);
}
return b;
}
protected void flushImpl() {
if (source != null) {
source.drain();
}
}
public int getPosition() {
int pos = 0;
if (source != null) {
pos = (int) (source.getMicrosecondPosition() / 1000);
}
return pos;
}
public void setLineGain(float gain) {
if (source != null) {
if (source.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl volControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
float newGain = Math.min(Math.max(gain, volControl.getMinimum()), volControl.getMaximum());
volControl.setValue(newGain);
}
}
}
public float getMaxGain() {
// boolean test = false;
// try {
// if (source == null) {
// test = true;
// if(testSource == null) {
// test();
// createSource2(false);
// } else {
// source = testSource;
// }
// }
// } catch(Exception e) {
// System.out.println(e.getMessage());
// }
// try {
// if (source != null) {
// FloatControl volControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
// maxGain = volControl.getMaximum();
// } else {
// int i = 0;
// }
// } catch(Exception e) {
// System.out.println(e.getMessage());
// }
// if(test) {
// testSource = source;
// source = null;
// }
return 6.0f;
}
public float getMinGain() {
// float minGain = -99999.0f;
// boolean test = false;
// try {
// if (source == null) {
// test = true;
// if(testSource == null) {
// test();
// createSource2(false);
// } else {
// source = testSource;
// }
// }
// } catch(Exception e) {
// System.out.println(e.getMessage());
// }
// try {
// if (source != null) {
// FloatControl volControl = (FloatControl) source.getControl(FloatControl.Type.MASTER_GAIN);
// minGain = volControl.getMinimum();
// } else {
// int i = 0;
// }
// } catch(Exception e) {
// System.out.println(e.getMessage());
// }
// if(test) {
// testSource = source;
// source = null;
// }
return -80.0f;
}
/**
* Runs a short test by playing a short silent sound.
*/
public void test()
throws JavaLayerException {
try {
open(new AudioFormat(22050, 16, 1, true, false));
short[] data = new short[22050 / 10];
write(data, 0, data.length);
flush();
close();
} catch (RuntimeException ex) {
throw new JavaLayerException("Device test failed: " + ex);
}
}
}